package net.cathackers.devel.scmp.chat.impl.xmpp.iqcomponent;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import net.cathackers.devel.scmp.auth.exceptions.AuthenticationException;
import net.cathackers.devel.scmp.auth.impl.AuthenticationCredentialsImpl;
import net.cathackers.devel.scmp.auth.impl.jabber.JabberUserID;
import net.cathackers.devel.scmp.auth.interfaces.AuthenticationCredentials;
import net.cathackers.devel.scmp.auth.interfaces.UserID;
import net.cathackers.devel.scmp.factories.SCMPFactory;
import net.cathackers.devel.scmp.xmpp.IQPacket;
import net.cathackers.devel.scmp.xmpp.IQType;
import net.cathackers.devel.scmp.xmpp.Packet;
import net.cathackers.devel.scmp.xmpp.StanzaErrors;
import net.cathackers.devel.scmp.xmpp.XMPPConfiguration;

public class AuthenticationHandlerProcessor {
    private XMPPConfiguration conf_;
    private Logger logger_;

    public AuthenticationHandlerProcessor() {
	conf_ = SCMPFactory.getXMPPConfiguration(); 
	logger_ = SCMPFactory.getLogger(this);
    }


    public IQPacket getAutheticationMechanizmsStanza(IQPacket iqPacket) {
	// the same iqPacket will be returned

	/*
	 * 
	 * <iq type="get" id="1324334"> <query xmlns="jabber:iq:auth">
	 * <username>foobar</username> </query> </iq>
	 * 
	 * 
	 * <iq type="result" id="1324334"> <query xmlns="jabber:iq:auth">
	 * <username>foobar</username> <resource/> <password/>
	 * ------------------------------------ if plain auth is supported
	 * <digest/> ------------------------------------ if digest auth is
	 * supported <token>32</token><sequence>34</sequence> ----- if
	 * zero-knowledge is supported </query> </iq>
	 */
	Document doc = iqPacket.getDocument();
	Element element = doc.getRootElement();
	
	if (element.element("query").element("username") != null) {
	    element.element("query").element("username").clearContent();
	} else {
	    element.element("query").addElement("username");
	}
	if (conf_.isAnonymousAuthenticationEnabled()) {
	    // TODO to be implemented ( or not ) later
	}

	if (conf_.isDigestAuthenticationEnabled()) {
	    element.element("query").addElement("digest");
	}

	if (conf_.isPlainAuthenticationEnabled()) {
	    element.element("query").addElement("resource");
	    element.element("query").addElement("password");
	}

	if (conf_.isZeroKnowlegeAuthenticationEnabled()) {
	    // TODO to be implemented ( or not ) later
	}

	iqPacket.setDocument(doc);
	iqPacket.switchDirection();
	iqPacket.setIQType(IQType.result);
	return iqPacket;
    }

    public AuthenticationCredentials getAuthenticationCredentials(
	    IQPacket iqPacket) {
	AuthenticationCredentials authenticationCredentials = null;

	/*
	 * Figure out the authentication mechanism
	 * 
	 * ##### Anonymous ##### <iq type="set" id="434"> <query
	 * xmlns="jabber:iq:auth"/> </iq> #####################
	 * 
	 * 
	 * ##### Plain ##### <iq type="set" id="31"> <query
	 * xmlns="jabber:iq:auth"/> <username>KAM</username> <password>I don't
	 * think so</password> <resource>Home</resource> </query </iq>
	 * #################
	 * 
	 * 
	 * ##### Digest ##### <iq type="set" id="233"> <query
	 * xmlns="jabber:iq:auth"/> <username>KAM</username>
	 * <resource>Home</username> <digest>SHA-1(I don't think
	 * so_StreadID)</digest> </query> </iq> ##################
	 */

	Document doc = iqPacket.getDocument();
	Element element = doc.getRootElement().element("query");

	// TODO digest , anony. and zero-knowledge will be implemented later

	/*
	 * String digest = element.elementText("digest"); String token =
	 * element.elementText("token"); String sequence =
	 * element.elementText("sequence");
	 */

	switch (getAuthenticationMechanism(iqPacket)) {
	/*
	 * case digest: break; case anonymous: break; case zeroKnowledge: break;
	 */
	case plain:
	    // Plain Authentication
	    String username = element.elementText("username");
	    String resource = element.elementText("resource");
	    String password = element.elementText("password");

	    String domain = SCMPFactory.getConfiguration()
		    .getChatServerDomain();
	    UserID userID = new JabberUserID(username + "@" + domain + "/"
		    + resource);
	    authenticationCredentials = new AuthenticationCredentialsImpl(
		    userID, password.getBytes());
	    break;
	case digest:
	    
	default:
	    // TODO No mechanism can be used from the packet
	    break;
	}
	return authenticationCredentials;
    }

    public Packet getSuccessfulAuthenticationStanza(IQPacket iqPacket) {
	/*
	 * 
	 * 
	 * ##### Anonymous ##### <iq type="result" id="434"> <query
	 * xmlns="jabber:iq:auth> <resource>randomResource</resource> </query>
	 * </iq> #####################
	 * 
	 * 
	 * ##### Plain ##### <iq type="result" id="31"> <query
	 * xmlns="jabber:iq:auth"/> </iq> #################
	 * 
	 * 
	 * ##### Digest ##### <iq type="result" id="31"> <query
	 * xmlns="jabber:iq:auth"/> </iq> ##################
	 */
	switch (getAuthenticationMechanism(iqPacket)) {
	case plain:
	case digest:
	    Document doc = DocumentHelper.createDocument();
	    Element rootElement = doc.addElement("iq");
	    rootElement.addAttribute("type", "result");
	    rootElement.addAttribute("id", iqPacket.getID());
	    Element element = rootElement.addElement("query");
	    element.addNamespace("", "jabber:iq:auth");
	    iqPacket.setDocument(doc);
	    iqPacket.switchDirection();
	    break;
	}
	return iqPacket;
    }

    private AuthenticationMechanisms getAuthenticationMechanism(
	    IQPacket iqPacket) {
	AuthenticationMechanisms authenticationMechanism = null;
	Document doc = iqPacket.getDocument();
	Element element = doc.getRootElement().element("query");
	String username = element.elementText("username");
	String resource = element.elementText("resource");
	String password = element.elementText("password");
	String digest = element.elementText("digest");
	String token = element.elementText("token");
	String sequence = element.elementText("sequence");

	// barf early if resource is not set, 
	
	if ((resource == null || resource.trim().length() == 0) 
		||(username == null || username.trim().length() == 0)) {
	    throw new AuthenticationException("no username or resource was set");
	}
	// be aware the order of checks determines which kind of authentication
	// will be recognized first
	if (conf_.isDigestAuthenticationEnabled() && (digest != null && digest.length() != 0)) {
	    // Digest Authentication
	    authenticationMechanism = AuthenticationMechanisms.digest;
	} else 
	if ((conf_.isPlainAuthenticationEnabled()) && (password != null && password.length() != 0)) {
	    // Plain Authentication
	    authenticationMechanism = AuthenticationMechanisms.plain;
	}

	return authenticationMechanism;
    }

    /*
     * ########## Error Region ########### Create a result packet as a response
     * of errors
     * 
     * 1- ##### Internal server error #####
     * 
     * <iq from='example.com' type='error' id='sess_1'> <session
     * xmlns='urn:ietf:params:xml:ns:xmpp-session'/> <error type='wait'>
     * <internal-server-error xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
     * </error> </iq> #################################
     * 
     * 
     * 
     * 2 - ##### Blocked User ##### <iq from='example.com' type='error'
     * id='sess_1'> <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
     * <error type='auth'> <forbidden
     * xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq>
     * ########################
     * 
     * 
     * 
     * 3 - ##### Conflict Resource ##### ( We will go with terminating the "old"
     * resource [ old session ] )
     * 
     * send this to the "old" resource <stream:error> <conflict
     * xmlns='urn:ietf:params:xml:ns:xmpp-streams'/> </stream:error>
     * </stream:stream>
     * 
     * Then proceed with response as a successful authentication with the "new"
     * resource
     */

    // TODO
    public IQPacket getInternalServerErrorStanza(IQPacket iqPacket) {
	StanzaErrors error = new StanzaErrors(
		StanzaErrors.Condition.internal_server_error);
	iqPacket.setError(error);
	iqPacket.switchDirection();
	iqPacket.setIQType(IQType.error);
	return iqPacket;
    }

    
    
    // TODO
    public IQPacket getResourceConflictErrorStanza(IQPacket iqPacket) {
	StanzaErrors error = new StanzaErrors(StanzaErrors.Condition.conflict);
	iqPacket.setError(error);
	iqPacket.switchDirection();
	iqPacket.setIQType(IQType.error);
	return iqPacket;
    }
    
    // TODO
    public IQPacket getUserNotFoundErrorStanza(IQPacket iqPacket) {
	StanzaErrors error = new StanzaErrors(StanzaErrors.Condition.not_acceptable);
	iqPacket.setError(error);
	iqPacket.switchDirection();
	iqPacket.setIQType(IQType.error);
	return iqPacket;
    }

    // TODO
    public IQPacket getBlockedUserErrorStanze(IQPacket iqPacket) {
	return getUserNotFoundErrorStanza(iqPacket);
    }

    public Packet getNotAuthorizedErrorStanza(IQPacket iqPacket) {
	StanzaErrors error = new StanzaErrors(
		StanzaErrors.Condition.not_authorized);
	iqPacket.setError(error);
	iqPacket.switchDirection();
	iqPacket.setIQType(IQType.error);
	return iqPacket;
    }
}